﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace EasyChat.Server
{
    class Server
    {
        List<MessageDesc> NewMessages;

        List<User> Users;
        List<Thread> UserThreads;
        List<Socket> UserSockets;

        Thread ListenerThread;
        Socket ListenerSocket;

        public Server()
        {
            Console.Title = "Easy Chat Server";

            Console.Write("Starting server on IP: " + Dns.GetHostEntry(Dns.GetHostName()).AddressList[0] + "...");
            NewMessages = new List<MessageDesc>();

            Users = new List<User>();
            UserThreads = new List<Thread>();
            UserSockets = new List<Socket>();

            ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ListenerSocket.Bind(new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], 4567));
            ListenerSocket.Listen(100);

            ListenerThread = new Thread(Listen);
            ListenerThread.IsBackground = true;
            ListenerThread.Start();
            Console.WriteLine("   Done!");

            Console.WriteLine("Listening for incoming connections...");

            while (true)
            {
                MessageDesc[] NewMessagesC = NewMessages.ToArray();
                foreach (MessageDesc message in NewMessagesC)
                {
                    string Message;
                    if(message.UserID == -1)
                        Message = message.Message;
                    else
                        Message = Users[message.UserID].Name + ": " + message.Message;

                    Console.WriteLine(Message);
                    SendMessage(Message, message.UserID);

                    NewMessages.Remove(message);
                }

                Thread.Sleep(100);
            }
        }

        private void SendMessage(string Message, int SenderID)
        {
            int id = 0;
            foreach (Socket socket in UserSockets)
            {
                try
                {
                    if (id != SenderID)
                        socket.Send(Encoding.ASCII.GetBytes(Message));
                    id++;
                }
                catch { }
            }
        }

        private void Listen()
        {
            while (true)
            {
                try
                {
                    Socket NewUserSocket = ListenerSocket.Accept();

                    User NewUser = new User();
                    NewUser.IP = "" + NewUserSocket.RemoteEndPoint;

                    byte[] NewUserName = new byte[4096];
                    NewUserSocket.Receive(NewUserName);
                    NewUser.Name = Encoding.ASCII.GetString(RemoveNull(NewUserName));

                    Users.Add(NewUser);
                    UserSockets.Add(NewUserSocket);
                    UserThreads.Add(new Thread(UserThread));
                    UserThreads[UserThreads.Count - 1].Start();

                    NewMessages.Add(new MessageDesc() { UserID = -1, Message = NewUser.Name + " connected from: " + NewUserSocket.RemoteEndPoint });
                }
                catch { }
            }
        }

        private void UserThread()
        {
            int ID = Users.Count - 1;

            while (true)
            {
                try
                {
                    byte[] message = new byte[4096 * 2];
                    UserSockets[ID].Receive(message);

                    string Message = Encoding.ASCII.GetString(RemoveNull(message));
                    NewMessages.Add(new MessageDesc() { UserID = ID, Message = Message });
                }
                catch
                {
                    UserSockets.Remove(UserSockets[ID]);
                    NewMessages.Add(new MessageDesc() { UserID = -1, Message = Users[ID].Name + " disconnected from: " + Users[ID].IP });
                    Users.Remove(Users[ID]);
                    UserThreads.Remove(UserThreads[ID]);
                    break;
                }
            }
        }

        public byte[] RemoveNull(byte[] data)
        {
            int i = data.Length - 1;
            while (data[i] == 0)
            {
                --i;
                if (i <= 0)
                    break;
            }
            byte[] newData = new byte[i + 1];
            Array.Copy(data, newData, i + 1);
            return newData;
        }
    }
}
